package anonymouschat.client.frames;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import anonymouschat.client.Client;
import anonymouschat.client.NickNameList;
import anonymouschat.exceptions.DecryptionException;
import anonymouschat.messages.TextMessage;
import anonymouschat.messages.clientrequests.UserLogoutRequest;
import anonymouschat.utils.LocalFileOps;
import anonymouschat.utils.Print;

/**
 * Frame which contains a list of users and their nicknames Allows for adding
 * and deleting of users and nicknames
 * 
 * @author Winters
 * 
 */
public class NickListFrame extends JFrame implements ActionListener,
		DocumentListener, WindowListener {
	private static final long serialVersionUID = 1L;

	private Client client;

	private JPanel textPanel;

	private JPanel buttonPanel;

	private JPanel userIDPanel;

	private JPanel nickNamePanel;

	private JPanel sendMsgPanel;

	private JPanel showHistoryPanel;

	private JPanel deletePanel;

	private NickNameList nickList;

	private JLabel userIDLabel = new JLabel("UserID:");

	private JLabel nickNameLabel = new JLabel("Nick Name:");

	private JButton[] sendMsgButton;

	private JButton[] showHistoryButton;

	private JButton[] deleteButton;

	private JButton addButton;

	private JLabel pathLabel;

	private JTextField[] nickText;

	private JTextField[] idText;

	private List<JTextField> userIDList;

	private List<JTextField> nickNameList;

	private List<JButton> sendMsgButtonList;

	private List<JButton> showHistoryButtonList;

	private List<JButton> deleteButtonList;

	private static final int HEIGHT = 35;
	private HashMap<String, ArrayList<String>> tempChatLog;
	private HashMap<String, OneOnOneChatFrame> oneOnOneChatList;

	/**
	 * Create NickListFrame UI
	 * 
	 * @param userID
	 *            - the userid running the client app
	 * @param nickList
	 *            - if null, creates a new list
	 * @throws DecryptionException 
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	public NickListFrame(Client client) throws DecryptionException {
		this.client = client;
		this.tempChatLog = new HashMap<String, ArrayList<String>>();
		readNickList();
		initComponents();
	}

	private void initComponents() {
		setTitle("Friends of : " + client.getIdentity().getUserName());
		setLocation(0, 0);
		setVisible(true);
		setResizable(false);
		setLayout(new BorderLayout());
		textPanel = new JPanel();
		textPanel.setLayout(new GridLayout(1, 2, 0, 0));
		buttonPanel = new JPanel();
		buttonPanel.setLayout(new GridLayout(1, 3, 0, 0));
		setIDPanel(); // userID Panel
		setNickNamePanel();// nickName Panel
		setSendMsgPanel(); // sendMsg Panel
		setShowHistPanel(); // showHistory Panel
		setDelPanel(); // deleting Panel
		pathLabel = new JLabel("Routing Path: " + this.client.getPath());
		pathLabel.setPreferredSize(new Dimension(300, 15));

		addButton = new JButton("Add");
		addButton.setPreferredSize(new Dimension(300, HEIGHT));
		addButton.addActionListener(this);
		textPanel.add(userIDPanel);
		textPanel.add(nickNamePanel);
		buttonPanel.add(sendMsgPanel);
		buttonPanel.add(showHistoryPanel);
		buttonPanel.add(deletePanel);
		textPanel.setPreferredSize(new Dimension(300, HEIGHT
				* (nickList.getNickNameList().size() + 1)));
		buttonPanel.setPreferredSize(new Dimension(300, HEIGHT
				* (nickList.getNickNameList().size() + 1)));
		getContentPane().add(pathLabel, BorderLayout.NORTH);
		getContentPane().add(textPanel, BorderLayout.WEST);
		getContentPane().add(buttonPanel, BorderLayout.EAST);
		getContentPane().add(addButton, BorderLayout.SOUTH);
		pack();
		validate();
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				saveNickList();
				dumpChatLog();
				leaveSystem();
			}

			private void saveNickList() {
				try {
					LocalFileOps.writeNickList(client.getIdentity(), nickList);
				} catch (IOException e) {
					Print.error("Cannot save nickList.", e);
				}
			}
		});
	}

	/**
	 * show chat messages that clientInConnectionThread receives. save the
	 * message in temporary chat record.
	 * 
	 * @author Winters
	 * @param msg
	 */
	public void showTextMessage(TextMessage msg) {
		String userID = msg.getSrcUser();
		OneOnOneChatFrame tempOneOnOneChatFrame;
		if (oneOnOneChatList == null) {
			oneOnOneChatList = new HashMap<String, OneOnOneChatFrame>();
		}
		if (oneOnOneChatList.containsKey(userID) == true) // existing
			tempOneOnOneChatFrame = oneOnOneChatList.get(userID);
		else {
			tempOneOnOneChatFrame = new OneOnOneChatFrame(client,
					client.getIdentity(), msg.getSrcUser(), tempChatLog,
					oneOnOneChatList);
			oneOnOneChatList.put(userID, tempOneOnOneChatFrame);
		}

		String tempData = msg.getSrcUser() + "\n" + new Date() + "\n"
				+ msg.getPlaintextData() + "\n\n";
		String nickName = this.nickList.getNickNameFor(msg.getSrcUser());
		if (nickName == null) {
			tempData = msg.getSrcUser() + tempData;
		} else {
			tempData = nickName + tempData;
		}
		tempOneOnOneChatFrame.textArea.append(tempData);
		if (tempChatLog.containsKey(userID)) {
			tempChatLog.get(userID).add(tempData);
		} else {
			tempChatLog.put(userID, new ArrayList<String>());
			tempChatLog.get(userID).add(tempData);
		}
	}

	/**
	 * Tell the publication server that the user is leaving
	 */
	protected void leaveSystem() {
		UserLogoutRequest msg = new UserLogoutRequest(client.getIdentity());
		try {
			Print.debug(client.getIdentity().getUserName()
					+ "is about to send Logoff message");
			client.sendMessage(msg);
			client.getOutputStream().flush();
			client.getOutputStream().close();
		} catch (IOException e1) {
			Print.error(
					"Error occured when sending UserLeaveRequest object to server",
					e1);
		}
		Print.debug("Exiting. . .");
		System.exit(0);
	}

	/**
	 * Save tempChatLog to file.
	 * 
	 * @throws IOException
	 */
	public void dumpChatLog() {
		for (Map.Entry<String, ArrayList<String>> entry : tempChatLog
				.entrySet()) {
			String dstUserName = entry.getKey();
			ArrayList<String> msgList = entry.getValue();
			StringBuffer sb = new StringBuffer();
			for (String s : msgList) {
				sb.append(s);
			}
			try {
				LocalFileOps.appendChatHist(client.getIdentity(), dstUserName,
						sb.toString());
			} catch (ClassNotFoundException e) {
				Print.error("Error appending to local chat history", e);
			}
		}
		tempChatLog.clear();
	}

	/**
	 * Read the nick name list saved on disk
	 * @throws DecryptionException 
	 */
	private void readNickList() throws DecryptionException {
		nickList = LocalFileOps.getNickList(client.getIdentity());
		if (nickList == null) {
			nickList = new NickNameList();
		}
	}

	private void setDelPanel() {
		deletePanel = new JPanel();
		deletePanel.setLayout(new GridLayout(0, 1, 0, 0));
		deletePanel.add(new JLabel(""));
		List<String> tempList = nickList.getNickNameList();
		deleteButton = new JButton[tempList.size()];
		deleteButtonList = new ArrayList<JButton>();
		for (int i = 0; i < tempList.size(); i++) {
			deleteButton[i] = new JButton("Delete");
			deleteButton[i].addActionListener(this);
			deleteButtonList.add(deleteButton[i]);
			deletePanel.add(deleteButton[i]);
		}
	}

	private void setShowHistPanel() {
		showHistoryPanel = new JPanel();
		showHistoryPanel.setLayout(new GridLayout(0, 1, 0, 0));
		showHistoryPanel.add(new JLabel(""));
		List<String> tempList = nickList.getNickNameList();
		showHistoryButton = new JButton[tempList.size()];
		showHistoryButtonList = new ArrayList<JButton>();
		for (int i = 0; i < tempList.size(); i++) {
			showHistoryButton[i] = new JButton("History");
			showHistoryButton[i].addActionListener(this);
			showHistoryButtonList.add(showHistoryButton[i]);
			showHistoryPanel.add(showHistoryButton[i]);
		}

	}

	private void setSendMsgPanel() {
		sendMsgPanel = new JPanel();
		sendMsgPanel.setLayout(new GridLayout(0, 1, 0, 0));
		sendMsgPanel.add(new JLabel(""));
		List<String> tempList = nickList.getNickNameList();
		sendMsgButton = new JButton[tempList.size()];
		sendMsgButtonList = new ArrayList<JButton>();
		for (int i = 0; i < tempList.size(); i++) {
			sendMsgButton[i] = new JButton("Message");
			sendMsgButton[i].addActionListener(this);
			sendMsgButtonList.add(sendMsgButton[i]);
			sendMsgPanel.add(sendMsgButton[i]);
		}
	}

	private void setNickNamePanel() {
		nickNamePanel = new JPanel();
		nickNamePanel.setLayout(new GridLayout(0, 1, 0, 0));
		nickNamePanel.add(nickNameLabel);
		List<String> tempList = nickList.getNickNameList();
		nickText = new JTextField[tempList.size()];
		nickNameList = new ArrayList<JTextField>();
		for (int i = 0; i < tempList.size(); i++) {
			nickText[i] = new JTextField();
			nickText[i].setText(tempList.get(i).toString());
			nickText[i].addActionListener(this);
			nickText[i].getDocument().addDocumentListener(this);
			nickNameList.add(nickText[i]);
			nickNamePanel.add(nickText[i]);
		}

	}

	private void setIDPanel() {
		userIDPanel = new JPanel();
		userIDPanel.setLayout(new GridLayout(0, 1, 0, 0));
		userIDPanel.add(userIDLabel);
		List<String> tempList = nickList.getUserIDList();
		idText = new JTextField[tempList.size()];
		userIDList = new ArrayList<JTextField>();
		for (int i = 0; i < tempList.size(); i++) {
			idText[i] = new JTextField();
			idText[i].setText(tempList.get(i).toString());
			idText[i].addActionListener(this);
			idText[i].getDocument().addDocumentListener(this);
			userIDList.add(idText[i]);
			userIDPanel.add(idText[i]);
		}
	}

	/**
	 * show some txt on the user's oneOnOneChatWindow
	 * 
	 * @param userID
	 * @param msg
	 */
	public void showTxtOnUserWindow(String userID, String msg) {
		OneOnOneChatFrame tempOneOnOneChatFrame;
		if (oneOnOneChatList == null) {
			oneOnOneChatList = new HashMap<String, OneOnOneChatFrame>();
		}
		if (oneOnOneChatList.containsKey(userID) == true) {
			tempOneOnOneChatFrame = oneOnOneChatList.get(userID);
		} else {
			tempOneOnOneChatFrame = new OneOnOneChatFrame(client,
					client.getIdentity(), userID, tempChatLog, oneOnOneChatList);
			oneOnOneChatList.put(userID, tempOneOnOneChatFrame);
		}
		tempOneOnOneChatFrame.showMsgInWindow(msg);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		int i = -1;
		if (e.getSource() == addButton) { // if click on add button
			nickList.getUserIDList().add("");
			nickList.getNickNameList().add("");

			JTextField tempTextField = new JTextField();
			tempTextField.getDocument().addDocumentListener(this);
			userIDList.add(tempTextField);
			userIDPanel.add(tempTextField);

			tempTextField = new JTextField();
			tempTextField.getDocument().addDocumentListener(this);
			nickNameList.add(tempTextField);
			nickNamePanel.add(tempTextField);

			JButton tempButton = new JButton("Delete");
			tempButton.addActionListener(this);
			deleteButtonList.add(tempButton);
			deletePanel.add(tempButton);

			tempButton = new JButton("Message");
			tempButton.addActionListener(this);
			sendMsgButtonList.add(tempButton);
			sendMsgPanel.add(tempButton);

			tempButton = new JButton("History");
			tempButton.addActionListener(this);
			showHistoryButtonList.add(tempButton);
			showHistoryPanel.add(tempButton);

			textPanel.setPreferredSize(new Dimension(300, HEIGHT
					* (nickList.getNickNameList().size() + 1)));
			buttonPanel.setPreferredSize(new Dimension(300, HEIGHT
					* (nickList.getNickNameList().size() + 1)));
			Print.debug("nickList size: " + nickList.getNickNameList().size());
			pack();
			validate();
		} else {
			i = deleteButtonList.indexOf(e.getSource());
			if (i != -1) { // if click on delete button
				nickList.getNickNameList().remove(i);
				nickList.getUserIDList().remove(i);

				userIDPanel.remove(userIDList.get(i));
				nickNamePanel.remove(nickNameList.get(i));
				sendMsgPanel.remove(sendMsgButtonList.get(i));
				showHistoryPanel.remove(showHistoryButtonList.get(i));
				deletePanel.remove(deleteButtonList.get(i));

				userIDList.remove(i);
				nickNameList.remove(i);
				sendMsgButtonList.remove(i);
				showHistoryButtonList.remove(i);
				deleteButtonList.remove(i);

				textPanel.setPreferredSize(new Dimension(300, HEIGHT
						* (nickList.getNickNameList().size() + 1)));
				buttonPanel.setPreferredSize(new Dimension(300, HEIGHT
						* (nickList.getNickNameList().size() + 1)));
				Print.debug("nickList size: "
						+ nickList.getNickNameList().size());
				pack();
				validate();
				return;
			}
			i = sendMsgButtonList.indexOf(e.getSource());
			if (i != -1) { // if click on send message button
				String dst = userIDList.get(i).getText();
				// open one on one
				OneOnOneChatFrame tempOneOnOneChatFrame;
				if (oneOnOneChatList == null) {
					oneOnOneChatList = new HashMap<String, OneOnOneChatFrame>();
				}
				if (oneOnOneChatList.containsKey(dst) == true) {
					tempOneOnOneChatFrame = oneOnOneChatList.get(dst);
				} else {
					tempOneOnOneChatFrame = new OneOnOneChatFrame(client,
							client.getIdentity(), dst, tempChatLog,
							oneOnOneChatList);
					oneOnOneChatList.put(dst, tempOneOnOneChatFrame);
				}

				return;
			}
			i = showHistoryButtonList.indexOf(e.getSource());
			if (i != -1) { // if click on show history button
				try {
					new LogFrame(client.getIdentity(), userIDList.get(i)
							.getText());
				} catch (ClassNotFoundException e1) {
					Print.error("Error creating LogFrame", e1);
				} catch (IOException e1) {
					Print.error("IOexception creating LogFrame", e1);
				}
				return;
			}
		}
	}

	@Override
	public void changedUpdate(DocumentEvent e) {
		textChangeDetectioner(e);
	}

	@Override
	public void insertUpdate(DocumentEvent e) {
		textChangeDetectioner(e);
	}

	@Override
	public void removeUpdate(DocumentEvent e) {
		textChangeDetectioner(e);
	}

	private void textChangeDetectioner(DocumentEvent e) {
		for (int i = 0; i < nickList.getNickNameList().size(); i++) {
			if (e.getDocument().equals(userIDList.get(i).getDocument())) {
				nickList.getUserIDList().set(i, userIDList.get(i).getText());
			} else {
				if (e.getDocument().equals(nickNameList.get(i).getDocument())) {
					nickList.getNickNameList().set(i,
							nickNameList.get(i).getText());
				}
			}
		}
	}

	@Override
	public void windowActivated(WindowEvent arg0) {

	}

	@Override
	public void windowClosed(WindowEvent arg0) {

	}

	@Override
	public void windowClosing(WindowEvent arg0) {
		try {
			LocalFileOps.writeNickList(client.getIdentity(), nickList);
		} catch (IOException e) {
			Print.error("Error writing NickList", e);
		}
	}

	@Override
	public void windowDeactivated(WindowEvent arg0) {

	}

	@Override
	public void windowDeiconified(WindowEvent arg0) {

	}

	@Override
	public void windowIconified(WindowEvent arg0) {

	}

	@Override
	public void windowOpened(WindowEvent arg0) {

	}

	public Client getClient() {
		return this.client;
	}
}